package reflect;

import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Collectors;

//https://juejin.cn/post/6977679823132950536
@SuppressWarnings("all")
public class ReflectTest {

    //自定义三个注解
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface AnnA {}

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface AnnB {}

    @Inherited
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface AnnC {}

    //接口
    interface ICar {
        void combine();
    }

    //父类
    @AnnC
    static class Car<K, V> {
        private String design = "设计稿";
        public String engine = "发动机";

        public void run(long kilometer) {
            System.out.println("the car has run: " + kilometer + "km");
        }
    }

    @AnnA
    @AnnB
    static class Benz extends Car<String, Integer> implements ICar {

        //内部类
        public class Gift {}

        private String name = "奔驰";
        public String color = "白色";

        public Benz() {}

        private Benz(String name) {
            this.name = name;
        }

        public Benz(String name, String color) {
            this.name = name;
            this.color = color;
        }

        @Override
        public void combine() {
            System.out.println("组装一台奔驰");
        }

        private void show(String param) {
            System.out.println("我是私有方法show():" + param);
        }
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        /*1、类
        1.1、获取类对象
            方式一、Benz.class类获取
            方式二、benz.getClass对象获取
            方式三、Class.forName静态获取
            说明、除了类获取不会初始化静态属性其他两种方式会使静态属性初始化一次
         */
        System.out.println(Benz.class);
        System.out.println(new Benz().getClass());
        System.out.println(Class.forName("java.lang.String"));

        /*1.2、获取类名
        benz.getSimpleName()
         */
        System.out.println(new Benz().getClass().getSimpleName());

        /*1.3、获取类路径
            方式一、benz.getName()
            方式二、benz.getCanonicalName()
            说明、对内部类来说getName获取的是路径.类名$内部类
         */
        System.out.println(Benz.class.getName());
        System.out.println(Benz.class.getCanonicalName());
        System.out.println(Benz.Gift.class.getName());
        System.out.println(Benz.Gift.class.getCanonicalName());

        /*1.4、获取父类名
        benz.getSuperclass().getSimpleName()
         */
        System.out.println(Benz.class.getSuperclass().getSimpleName());

        /*1.5、获取接口
        benz.getInterfaces()
         */
        Class<?>[] interfaces = Benz.class.getInterfaces();
        System.out.println(Arrays.stream(interfaces).map((Function<Class<?>, Object>) Class::getName).collect(Collectors.toList()));
        System.out.println(Arrays.stream(interfaces).map((Function<Class<?>, Object>) Class::getCanonicalName).collect(Collectors.toList()));

        /*1.6、创建实例对象
        benz.getDeclaredConstructor()获取构造函数
        constructor.newInstance(param...)创建实例对象
        注意、
            1、带Declared获取的是本类所有属性和方法
            2、不带Declared获取的是包含继承的所有public修饰的属性和方法
            3、调用constructor/field/method.setAccessible(true)后可访问private修饰的属性和方法
         */
        Constructor<Benz> benzConstructor = Benz.class.getDeclaredConstructor();
        Benz benz = benzConstructor.newInstance();
        benz.color = "红色";
        benz.combine();
        System.out.println(benz.color);


        /*2、属性
        2.1、获取单个属性
        benz.getDeclaredField(filedName)
         */
        System.out.println(Benz.class.getDeclaredField("name").getName());

        /*2.2、获取多个属性
        benz.getDeclaredFields()获取本类所有属性[Declared]
        benz.getFields()获取本类及父类所有public属性
         */
        System.out.println(Arrays.stream(Benz.class.getDeclaredFields()).map((Function<Field, Object>) Field::getName).collect(Collectors.toList()));
        System.out.println(Arrays.stream(Benz.class.getFields()).map((Function<Field, Object>) Field::getName).collect(Collectors.toList()));

        /*2.3、允许访问和设置private属性值&给属性设置值
            2.3.1、field.setAccessible(true)允许访问私有属性
            2.3.2、field.set(benz, value)为属性赋值
         */
        Field name = benz.getClass().getDeclaredField("name");
        //name.set(benz, "梅赛德斯奔驰");//IllegalAccessException
        name.setAccessible(true);
        name.set(benz, "梅赛德斯奔驰");
        System.out.println(benz.name);

        /*2.4、获取属性类型
        field.getType().getName()
         */
        System.out.println(name.getType().getName());

        /*2.5、获取对象中的该属性值
        field.get(benz)
         */
        System.out.println(name.get(benz));


        /*3、方法
        3.1、获取单个方法
            3.1.1、benz.getDeclaredMethod//获取本类所有方法
            3.1.2、benz.getMethod//获取本类及父类所有public方法
         */
        System.out.println(Benz.class.getDeclaredMethod("show", String.class));
        System.out.println(Benz.class.getMethod("combine"));

        /*3.2、获取多个方法
            3.2.1、getDeclaredMethods//获取本类所有方法
            3.2.2、getMethods//获取本类及父类所有public方法
         */
        System.out.println(Arrays.stream(Benz.class.getDeclaredMethods()).map((Function<Method, Object>) Method::getName).collect(Collectors.toList()));
        System.out.println(Arrays.stream(Benz.class.getMethods()).map((Function<Method, Object>) Method::getName).collect(Collectors.toList()));

        /*3.3、方法调用
        method.invoke(benz, param...)
         */
        Method show = benz.getClass().getDeclaredMethod("show", String.class);
        //show.invoke(benz, "参数");//IllegalAccessException
        show.setAccessible(true);
        show.invoke(benz, "参数");


        /*4、构造方法
        4.1、获取单个构造方法
        getDeclaredConstructor//获取本类单个构造方法
        getConstructor//获取本类单个public构造方法
         */
        System.out.println(benz.getClass().getDeclaredConstructor());
        System.out.println(benz.getClass().getDeclaredConstructor(String.class));
        System.out.println(benz.getClass().getDeclaredConstructor(String.class, String.class));
        //System.out.println(benz.getClass().getDeclaredConstructor(String.class, String.class, String.class));//NoSuchMethodExceptio
        System.out.println(benz.getClass().getConstructor());
        //System.out.println(benz.getClass().getConstructor(String.class));//NoSuchMethodException
        System.out.println(benz.getClass().getConstructor(String.class, String.class));

        /*4.2、获取多个构造方法
        getDeclaredConstructors//获取本类的所有构造方法
        getConstructors//获取不包含父类的全部public构造方法
         */
        System.out.println(Arrays.toString(benz.getClass().getDeclaredConstructors()));
        System.out.println(Arrays.toString(benz.getClass().getConstructors()));

        /*4.3、构造方法实例化对象
        constructor.newInstance(param...)
         */
        Constructor<? extends Benz> constructor = benz.getClass().getDeclaredConstructor(String.class);
        //System.out.println(constructor.newInstance("").color);//IllegalAccessException
        constructor.setAccessible(true);
        System.out.println(constructor.newInstance("").color);
        System.out.println(benz.getClass().getDeclaredConstructor(String.class, String.class).newInstance("", "棕色").color);


        /*5、泛型
        5.1、获取父类的泛型
         */
        System.out.println(Arrays.stream(((ParameterizedType) benz.getClass().getGenericSuperclass()).getActualTypeArguments()).map((Function<Type, Object>) Type::getTypeName).collect(Collectors.toList()));


        /*6、注解
        6.1、获取单个注解
            6.1.1、getDeclaredAnnotation//获取本类单个注解
            6.1.2、getAnnotation//获取本类及父类的单个注解
         */
        System.out.println(benz.getClass().getAnnotation(AnnA.class).annotationType().getName());
        System.out.println(benz.getClass().getAnnotation(AnnC.class).annotationType().getName());
        System.out.println(benz.getClass().getDeclaredAnnotation(AnnB.class).annotationType().getName());

        /*6.2、获取全部注解
        getDeclaredAnnotations//获取本类所有注解
        getAnnotations//获取本类及父类所有注解
         */
        System.out.println(Arrays.stream(benz.getClass().getDeclaredAnnotations()).map((Function<Annotation, Object>) annotation -> annotation.annotationType().getName()).collect(Collectors.toList()));
        System.out.println(Arrays.stream(benz.getClass().getAnnotations()).map((Function<Annotation, Object>) annotation -> annotation.annotationType().getName()).collect(Collectors.toList()));


        /** api总结 */
        //Declared--包含则获取本类所有属性方法/不包含则获取本类及父类所有public属性和方法
        //.setAccessible(true)--解除私有限定

        //Constructor[] getConstructors()--获取所有public构造方法
        //Constructor[] getDeclaredConstructors()--获取所有构造方法
        //Constructor getConstructor(Class... parameterTypes)--获取某个public构造方法
        //Constructor getDeclaredConstructor(Class... parameterTypes)--获取某个构造方法
        //Constructor.newInstance(Object... args)--调用构造方法创建实例

        //Field[] getFields()--获取所有public字段
        //Field[] getDeclaredFields()--获取所有字段
        //Field getField(String fieldName)--获取某个公有字段
        //Field getDeclaredField(String fieldName)--获取某个字段
        //Object get(Object obj)--获取字段值
        //void set(Object obj, Object value)--设置字段值

        //Method[] getMethods()--获取所有public方法
        //Method[] getDeclaredMethods()--获取所有方法
        //Method getMethod(String name,Class<?>... parameterTypes)--获取某个public方法
        //Method getDeclaredMethod(String name, Class<?>... parameterTypes)--获取某个方法
        //Method.invoke(Object obj, Object... args)--调用方法

        //getAnnotation--本类及父类注解
        //getDeclaredAnnotation--本类注解
        //getAnnotations--本类及父类全部注解
        //getDeclaredAnnotations--本类所有注解
    }
}
